#include "chartParser.h"
#include "note.h"

ChartParser::ChartParser()
{

}

ChartParser::InstrumentTrack ChartParser::parse( QString chartFile, QString &songPath )
{
	QFile file(chartFile); 

	// TODO : determine the note scaling automatically
	Note::SCALE = 15.0;

	if (file.open(QFile::ReadOnly)) 
	{
		QTextStream stream(&file);
		QString line;

		do 
		{
			line = stream.readLine();

			if (line=="[Song]")
			{
				parseSongSection(stream);
			}
			else if (line=="[SyncTrack]")
			{
				parseSyncTrackSection(stream);
			}
			else if (line=="[Events]")
			{
				parseEventsSection(stream);
			}
			else if (line=="[ExpertSingle]")
			{
				parseInstrumentSection(_expertTrack, stream);
			}
			// TODO : other sections
			
		}
		while (!line.isNull());
	}
	else
	{

	}

	songPath = _musicStream;

	// apply sound offset
	Note::SOUNDOFFSET = _offset;

	return _expertTrack;
}

void ChartParser::parseInstrumentSection(InstrumentTrack &track, QTextStream &stream)
{
	QString line;
	double lastNoteTime = -1.0;
	long lastNoteTicPosition = 0;

	long lastNoteBeatChangePosition = 0;
	double lastNoteBeatChangeTime = 0.0;

	double lastTimeUnit = 0.0;

	do 
	{
		line = stream.readLine();
		line.replace("\t","");
		QStringList arguments = line.split(" ");
		if (arguments.size() == 5)
		{
			QString elementType = arguments[2];

			// face off information, we dont use that ...
			if (elementType=="S")
			{
			}
			// note information
			else if (elementType=="N")
			{
				long noteTicPosition = arguments[0].toLong();
				int noteTrack = arguments[3].toLong();
				int noteLength = arguments[4].toLong();
				long timeUnitsPosition = noteTicPosition/12;
				long timeUnitsLength = noteLength/12;



				bool timeUnitChanged = false;

				// find out which time unit we have to use
				TimeUnitMapIterator it(_timeUnits);
				double timeUnit = 0.0;
				while(it.hasNext())
				{
					it.next();
					long bpmTicPosition = it.key();
					if (bpmTicPosition<=noteTicPosition)
					{
						timeUnit = it.value();
					}
					else
					{
						break;
					}
				}

				//long deltaTic = noteTicPosition - lastNoteTicPosition;
				//long deltaTimeUnits = deltaTic/12;
				//double noteTimePosition = lastNoteTime + deltaTimeUnits*timeUnit;

				long deltaTic = noteTicPosition - lastNoteBeatChangePosition;
				long deltaTimeUnits = deltaTic/12;
				double noteTimePosition = lastNoteBeatChangeTime + deltaTimeUnits*timeUnit;

				// duration is easy
				double noteDuration = timeUnitsLength*timeUnit;

				// store the note time and tic position if we had a bpm change
				if (timeUnit != lastTimeUnit)
				{
					timeUnitChanged = true;
					lastNoteBeatChangePosition = noteTicPosition;
					lastNoteBeatChangeTime = noteTimePosition;
				}


				Note* note = new Note(noteTimePosition, noteDuration, noteTrack);
				if (lastNoteTime == noteTimePosition)
				{
					note->setChord(true);
				}

				track.insert(std::make_pair(noteTimePosition, note));

				lastNoteTime = noteTimePosition;
				lastNoteTicPosition = noteTicPosition;
				lastTimeUnit = timeUnit;

			}
		}

	} while (!line.contains("}"));
}

void ChartParser::parseEventsSection(QTextStream &stream)
{
	QString line;
	do 
	{
		line = stream.readLine();
		line.replace("\t","");
		QStringList arguments = line.split(" ");
		if (arguments.size() == 4)
		{
			QString syncType = arguments[2];

			// beat per second information
			if (syncType=="E")
			{
				_events[arguments[0].toLong()] = arguments[3];
			}
		}

	} while (!line.contains("}"));
}

void ChartParser::parseSyncTrackSection(QTextStream &stream)
{
	QString line;
	do 
	{
		line = stream.readLine();
		line.replace("\t","");
		QStringList arguments = line.split(" ");
		if (arguments.size() == 4)
		{
			QString syncType = arguments[2];

			// beat per second information
			if (syncType=="B")
			{
				_bpms[arguments[0].toLong()] = float(arguments[3].toInt())/1000.0;
			}
			// measure per beat information
			else if (syncType=="TS")
			{
				_measures[arguments[0].toLong()] = arguments[3].toLong();
			}
		}

	} while (!line.contains("}"));

	// now we determine the time units for each sections
	BpmMapIterator it(_bpms);
	while (it.hasNext())
	{
		it.next();
		double baseBPM = it.value();
		double BPS = baseBPM/60.0;
		double beatDuration = 1.0/BPS;

		// find out the number of beats per block
		MeasuresMapIterator it2(_measures);
		int beatPerBlock = 0;
		while (it2.hasNext())
		{
			it2.next();
			if (it2.key() <= it.key())
			{
				beatPerBlock = it2.value();
			}
			else
			{
				break;
			}
		}

		// now that we know the number of beats per block, compute the block duration
		double blockDuration = double(beatPerBlock)*beatDuration;

		// compute the time unit : the duration in seconds of 1/64th of a block
		double timeUnit = blockDuration/64.0;

		// store that in the time unit maps
		_timeUnits[it.key()] = timeUnit;

	}
}

void ChartParser::parseSongSection(QTextStream &stream)
{
	QString line;
	do 
	{
		line = stream.readLine();
		line.replace("\t","");
		QStringList arguments = line.split(" = ");
		QString fieldName = arguments[0];

		if (fieldName == "Name")
		{
			_songName = arguments[1].replace("\"","");
		}
		if (fieldName == "Artist")
		{
			_artistName = arguments[1].replace("\"","");
		}
		if (fieldName == "Charter")
		{
			_charterName = arguments[1].replace("\"","");
		}
		if (fieldName == "Offset")
		{
			_offset = arguments[1].toLong();
		}
		if (fieldName == "Player2")
		{
			_player2Instrument = arguments[1].replace("\"","");
		}
		if (fieldName == "Difficulty")
		{
			_difficulty = arguments[1].toLong();
		}
		if (fieldName == "PreviewStart")
		{
			_previewStart = arguments[1].toFloat();
		}
		if (fieldName == "PreviewEnd")
		{
			_previewEnd = arguments[1].toFloat();
		}
		if (fieldName == "Resolution")
		{
			_resolution = arguments[1].toLong();
		}
		if (fieldName == "Genre")
		{
			_genre = arguments[1].replace("\"","");
		}
		if (fieldName == "MediaType")
		{
			_mediaType = arguments[1].replace("\"","");
		}
		if (fieldName == "MusicStream")
		{
			_musicStream = arguments[1].replace("\"","");
		}

		
	}
	while (!line.contains("}"));

}